;
; hdldr.asm
;
;  Created on: 2021年11月5日
;      Author: ThinkPad
;
org 0100h
jmp LABEL_START

%include "load_inc.asm"
%include "pm.inc"

TRANS_SECT_NR equ 2
SECT_BUF_SIZE equ TRANS_SECT_NR * 512

disk_address_packet: db 0x10
	db 0
sect_cnt:		db	TRANS_SECT_NR
	db 0
	dw KERNEL_FILE_OFF
	dw KERNEL_FILE_SEG
lba_addr: dd 0
	dd 0

LABEL_GDT: Descriptor 0, 0, 0
LABEL_DESC_FLAT_C: Descriptor 0, 0fffffh, DA_CR | DA_32 | DA_LIMIT_4K
LABEL_DESC_FLAT_RW: Descriptor 0, 0fffffh, DA_DRW | DA_32 | DA_LIMIT_4K
LABEL_DESC_VIDEO: Descriptor 0B8000h, 0ffffh, DA_DRW | DA_DPL3

GdtLen equ $ - LABEL_GDT
GdtPtr dw GdtLen - 1
	dd LOADER_PHY_ADDR + LABEL_GDT

SelectorFlatC equ LABEL_DESC_FLAT_C - LABEL_GDT
SelectorFlatRW equ LABEL_DESC_FLAT_RW - LABEL_GDT
SelectorVideo equ LABEL_DESC_VIDEO - LABEL_GDT + SA_RPL3

BaseOfStack equ 0100h

err:
	mov dh, 5
	call real_mode_disp_str
	jmp $

LABEL_START:
	mov ax, cs
	mov ds, ax
	mov es, ax
	mov ss, ax
	mov sp, BaseOfStack

	mov dh, 0
	call real_mode_disp_str

	mov ebx, 0
	mov di, _MemChkBuf

.MemChkLoop:
	mov eax, 0E820h
	mov ecx, 20
	mov edx, 0534D4150h
	int 15h

	jc .MemChkFail
	add di, 20
	inc dword [_dwMCRNumber]
	cmp ebx, 0
	jne .MemChkLoop
	jmp .MemChkOK
.MemChkFail:
	mov dword [_dwMCRNumber], 0
.MemChkOK:
	mov eax, [fs:SB_ROOT_INODE]
	call get_inode

	mov dword [disk_address_packet + 8], eax
	call read_sector

	mov si, KernelFileName
	push bx
.str_cmp:
	add bx, [fs:SB_DIR_ENT_FNAME_OFF]
.1:
	lodsb
	cmp al, byte [es:bx]
	jz .2
	jmp .different
.2:
	cmp al, 0
	jz .found
	inc bx
	jmp .1
.different:
	pop bx
	add bx, [fs:SB_DIR_ENT_SIZE]
	sub ecx, [fs:SB_DIR_ENT_SIZE]
	jz .not_found
	push bx
	mov si, KernelFileName
	jmp .str_cmp
.not_found:
	mov dh, 3
	call real_mode_disp_str
	jmp $
.found:
	pop bx
	add bx, [fs:SB_DIR_ENT_INODE_OFF]
	mov eax, [es:bx]
	call get_inode
	mov dword [disk_address_packet + 8], eax

load_kernel:
	call read_sector
	cmp ecx, SECT_BUF_SIZE
	jl .done
	sub ecx, SECT_BUF_SIZE
	add word [disk_address_packet + 4], SECT_BUF_SIZE
	jc .1
	jmp .2
.1:
	add word [disk_address_packet + 6], 1000h
.2:
	add dword [disk_address_packet + 8], TRANS_SECT_NR
	jmp load_kernel
.done:
	mov dh, 2
	call real_mode_disp_str

	lgdt [GdtPtr]

	cli

	in al, 92h
	or al, 00000010b
	out 92h, al

	mov eax, cr0
	or eax, 1
	mov cr0, eax

	jmp dword SelectorFlatC:(LOADER_PHY_ADDR + LABEL_PM_START)

	jmp $

wSectorNo dw 0
bOdd db 0
dwKernelSize dd 0

KernelFileName db "kernel.bin", 0
MessageLength equ 9
LoadMessage: db "Loading  "
Message1		db	"         "
Message2		db	"in HD LDR"
Message3		db	"No KERNEL"
Message4		db	"Too Large"
Message5		db	"Error 0  "

clear_screen:
	mov ax, 0x600
	mov bx, 0x700
	mov cx, 0
	mov dx, 0x184f
	int 0x10
	ret

real_mode_disp_str:
	mov ax, MessageLength
	mul dh
	add ax, LoadMessage
	mov bp, ax
	mov ax, ds
	mov es, ax
	mov cx, MessageLength
	mov ax, 0x1301
	mov bx, 0x7
	mov dl, 0
	int 0x10
	ret

read_sector:
	xor ebx, ebx

	mov dword [disk_address_packet + 12], 0

	mov ah, 0x42
	mov dl, 0x80
	mov si, disk_address_packet
	int 0x13

	mov ax, [disk_address_packet + 6]
	mov es, ax
	mov bx, [disk_address_packet + 4]
	ret

get_inode:
	dec eax
	mov bl, [fs:SB_INODE_SIZE]
	mul bl
	mov edx, SECT_BUF_SIZE
	sub edx, dword [fs:SB_INODE_SIZE]
	cmp eax, edx
	jg err
	push eax

	mov ebx, [fs:SB_NR_IMAP_SECTS]
	mov edx, [fs:SB_NR_SMAP_SECTS]
	lea eax, [ebx + edx + ROOT_BASE + 2]
	mov dword [disk_address_packet + 8], eax
	call read_sector

	pop eax

	mov edx, dword [fs:SB_INODE_ISIZE_OFF]
	add edx, ebx
	add edx, eax
	mov ecx, [es:edx]

	add ax, word [fs:SB_INODE_START_OFF]

	add bx, ax
	mov eax, [es:bx]
	add eax, ROOT_BASE
	ret

[SECTION .s32]
ALIGN 32
[BITS 32]

LABEL_PM_START:
	mov ax, SelectorVideo
	mov gs, ax

	mov ax, SelectorFlatRW
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov ss, ax
	mov esp, TopOfStack

	call DispMemInfo
	call SetupPaging

	call InitKernel

	mov dword [BOOT_PARAM_ADDR], BOOT_PARAM_MAGIC
	mov eax, [dwMemSize]
	mov [BOOT_PARAM_ADDR + 4], eax
	mov eax, KERNEL_FILE_SEG
	shl eax, 4
	add eax, KERNEL_FILE_OFF
	mov [BOOT_PARAM_ADDR + 8], eax

	jmp SelectorFlatC:KRNL_ENT_PT_PHY_ADDR

DispAL:
	push ecx
	push edx
	push edi

	mov edi, [dwDispPos]

	mov ah, 0Fh
	mov dl, al
	shr al, 4
	mov ecx, 2
.begin:
	and al, 01111b
	cmp al, 9
	ja .1
	add al, '0'
	jmp .2
.1:
	sub al, 0Ah
	add al, 'A'
.2:
	mov [gs:edi], ax
	add edi, 2

	mov al, dl
	loop .begin

	mov [dwDispPos], edi
	pop edi
	pop edx
	pop ecx

	ret

DispInt:
	mov eax, [esp + 4]
	shr eax, 24
	call DispAL

	mov eax, [esp + 4]
	shr eax, 16
	call DispAL

	mov eax, [esp + 4]
	shr eax, 8
	call DispAL

	mov eax, [esp + 4]
	call DispAL

	mov ah, 07h
	mov al, 'h'
	push edi
	mov edi, [dwDispPos]
	mov [gs:edi], ax
	add edi, 4
	mov [dwDispPos], edi
	pop edi

	ret

DispStr:
	push ebp
	mov ebp, esp

	push ebx
	push esi
	push edi

	mov esi, [ebp + 8]
	mov edi, [dwDispPos]
	mov ah, 0Fh
.1:
	lodsb
	test al, al
	jz .2
	cmp al, 0Ah
	jnz .3
	push eax
	mov eax, edi
	mov bl, 160
	div bl
	and eax, 0FFh
	inc eax
	mov bl, 160
	mul bl
	mov edi, eax
	pop eax
	jmp .1
.3:
	mov [gs:edi], ax
	add edi, 2
	jmp .1
.2:
	mov [dwDispPos], edi
	pop edi
	pop esi
	pop ebx
	pop ebp
	ret

DispReturn:
	push szReturn
	call DispStr
	add esp, 4
	ret

MemCpy:
	push ebp
	mov ebp, esp

	push esi
	push edi
	push ecx

	mov edi, [ebp + 8]
	mov esi, [ebp + 12]
	mov ecx, [ebp + 16]

.1:
	cmp ecx, 0
	jz .2
	mov al, [ds:esi]
	inc esi

	mov byte [es:edi], al
	inc edi

	dec ecx
	jmp .1
.2:
	mov eax, [ebp + 8]
	pop ecx
	pop edi
	pop esi
	mov esp, ebp
	pop ebp
	ret

DispMemInfo:
	push esi
	push edi
	push ecx

	push szMemChkTitle
	call DispStr
	add esp, 4

	mov esi, MemChkBuf
	mov ecx, [dwMCRNumber]
.loop:
	mov edx, 5
	mov edi, ARDStruct
.1:
	push dword [esi]
	call DispInt
	pop eax
	stosd
	add esi, 4
	dec edx
	cmp edx, 0
	jnz .1

	call DispReturn
	cmp dword [dwType], 1
	jne .2
	mov eax, [dwBaseAddrLow]
	add eax, [dwLengthLow]
	cmp eax, [dwMemSize]
	jb .2
	mov [dwMemSize], eax
.2:
	loop .loop

	call DispReturn
	push szRAMSize
	call DispStr
	add esp, 4

	push dword [dwMemSize]
	call DispInt
	add esp, 4

	pop ecx
	pop edi
	pop esi
	ret

SetupPaging:
	xor edx, edx
	mov eax, [dwMemSize]
	mov ebx, 400000h
	div ebx
	mov ecx, eax
	test edx, edx
	jz .no_remainder
	inc ecx
.no_remainder:
	push ecx
	mov ax, SelectorFlatRW
	mov es, ax
	mov edi, PAGE_DIR_BASE
	xor eax, eax
	mov eax, PAGE_TBL_BASE | PG_P | PG_USU | PG_RWW

.1:
	stosd
	add eax, 4096
	loop .1

	pop eax
	mov ebx, 1024
	mul ebx
	mov ecx, eax
	mov edi, PAGE_TBL_BASE
	xor eax, eax
	mov eax, PG_P | PG_USU | PG_RWW
.2:
	stosd
	add eax, 4096
	loop .2

	mov eax, PAGE_DIR_BASE
	mov cr3, eax
	mov eax, cr0
	or eax, 80000000h
	mov cr0, eax
	jmp short .3
.3:
	nop

	ret

InitKernel:
	xor esi, esi
	mov cx, word [KERNEL_FILE_PHY_ADDR + 2Ch]
	movzx ecx, cx
	mov esi, [KERNEL_FILE_PHY_ADDR + 1Ch]
	add esi, KERNEL_FILE_PHY_ADDR

.Begin:
	mov eax, [esi + 0]
	cmp eax, 0
	jz .NoAction
	push dword [esi + 010h]
	mov eax, [esi + 04h]
	add eax, KERNEL_FILE_PHY_ADDR
	push eax
	push dword [esi+08h]
	call MemCpy
	add esp, 12
.NoAction:
	add esi, 020h
	dec ecx
	jnz .Begin

	ret

[SECTION .data1]
ALIGN 32

LABEL_DATA:
	_szMemChkTitle: db "BaseAddrL BaseAddrH LengthLow LengthHigh   Type", 0Ah, 0
	_szRAMSize: db "RAM size: ", 0
	_szReturn: db 0Ah, 0

	_dwMCRNumber: dd 0
	_dwDispPos: dd (80 * 7 + 0) * 2
	_dwMemSize: dd 0
_ARDStruct:
	_dwBaseAddrLow: dd 0
	_dwBaseAddrHigh: dd 0
	_dwLengthLow: dd 0
	_dwLengthHigh: dd 0
	_dwType: dd 0
_MemChkBuf: times 256 db 0

szMemChkTitle equ LOADER_PHY_ADDR + _szMemChkTitle
szRAMSize equ LOADER_PHY_ADDR + _szRAMSize
szReturn equ LOADER_PHY_ADDR + _szReturn
dwDispPos equ LOADER_PHY_ADDR + _dwDispPos
dwMemSize		equ	LOADER_PHY_ADDR + _dwMemSize
dwMCRNumber		equ	LOADER_PHY_ADDR + _dwMCRNumber
ARDStruct		equ	LOADER_PHY_ADDR + _ARDStruct
	dwBaseAddrLow	equ	LOADER_PHY_ADDR + _dwBaseAddrLow
	dwBaseAddrHigh	equ	LOADER_PHY_ADDR + _dwBaseAddrHigh
	dwLengthLow	equ	LOADER_PHY_ADDR + _dwLengthLow
	dwLengthHigh	equ	LOADER_PHY_ADDR + _dwLengthHigh
	dwType		equ	LOADER_PHY_ADDR + _dwType
MemChkBuf		equ	LOADER_PHY_ADDR + _MemChkBuf

StackSpace: times 1000h db 0
TopOfStack equ LOADER_PHY_ADDR + $


